import * as THREE from 'three';

// 定义日志条目类型
interface LogEntry {
  message: string;
  timestamp: number;
  level: 'info' | 'warn' | 'error';
}

/**
 * 游戏调试工具类
 * 提供日志记录、性能监控和调试辅助功能
 */
export class Debug {
  // 单例实例
  private static instance: Debug;
  
  // 是否启用调试模式
  private static enabled: boolean = false;
  
  // 日志历史记录
  private static logs: LogEntry[] = [];
  
  // 最大日志条目数
  private static readonly MAX_LOGS: number = 100;
  
  // 调试图形对象容器
  private static debugObjects: THREE.Object3D[] = [];
  
  // 调试辅助器是否可见
  private static helpersVisible: boolean = true;
  
  // 性能监控相关
  private static perfMarks: Record<string, number> = {};
  private static perfMeasures: Record<string, number[]> = {};
  
  /**
   * 初始化调试工具
   * @param enabled 是否启用调试模式
   */
  public static init(enabled: boolean = false): void {
    Debug.enabled = enabled;
    Debug.instance = new Debug();
    Debug.log('调试工具已初始化');
  }
  
  /**
   * 捕获全局错误和未处理的Promise异常
   */
  public static captureErrors(): void {
    window.onerror = (message, source, lineno, colno, error) => {
      Debug.error(`全局错误: ${message}\n来源: ${source}:${lineno}:${colno}\n${error?.stack || ''}`);
      return false;
    };

    window.addEventListener('unhandledrejection', (event) => {
      Debug.error(`未处理的Promise异常: ${event.reason}`);
    });

    Debug.log('错误捕获器已启用');
  }
  
  // 获取单例实例
  public static getInstance(): Debug {
    if (!Debug.instance) {
      Debug.instance = new Debug();
    }
    return Debug.instance;
  }
  
  // 私有构造函数，防止外部实例化
  private constructor() {
    // 初始化工作
  }
  
  /**
   * 记录普通信息日志
   * @param message 日志消息
   */
  public static log(message: string): void {
    if (!Debug.enabled) return;
    
    Debug.addLogEntry(message, 'info');
  }
  
  /**
   * 记录警告日志
   * @param message 警告消息
   */
  public static warn(message: string): void {
    if (!Debug.enabled) return;
    
    Debug.addLogEntry(message, 'warn');
    console.warn(`[警告] ${message}`);
  }
  
  /**
   * 记录错误日志
   * @param message 错误消息
   */
  public static error(message: string): void {
    // 错误总是记录，不受启用状态影响
    Debug.addLogEntry(message, 'error');
    console.error(`[错误] ${message}`);
  }
  
  /**
   * 添加一条日志条目到历史记录
   * @param message 日志消息
   * @param level 日志级别
   */
  private static addLogEntry(message: string, level: 'info' | 'warn' | 'error'): void {
    // 创建新的日志条目
    const entry: LogEntry = {
      message,
      timestamp: Date.now(),
      level
    };
    
    // 添加到日志数组
    Debug.logs.push(entry);
    
    // 如果超过最大数量，删除最早的条目
    if (Debug.logs.length > Debug.MAX_LOGS) {
      Debug.logs.shift();
    }
    
    // 如果是信息级别，且调试模式已启用，打印到控制台
    if (level === 'info' && Debug.enabled) {
      const originalConsoleLog = console.log.bind(console);
      originalConsoleLog(`[信息] ${message}`);
    }
  }
  
  /**
   * 获取所有日志历史
   * @returns 日志条目数组
   */
  public static getLogs(): LogEntry[] {
    return [...Debug.logs];
  }
  
  /**
   * 清除所有日志历史
   */
  public static clearLogs(): void {
    Debug.logs = [];
  }
  
  /**
   * 启用或禁用调试功能
   * @param value 是否启用
   */
  public static setEnabled(value: boolean): void {
    Debug.enabled = value;
    
    // 更新辅助器可见性
    Debug.setHelpersVisible(value);
  }
  
  /**
   * 检查调试模式是否启用
   * @returns 是否启用
   */
  public static isEnabled(): boolean {
    return Debug.enabled;
  }
  
  /**
   * 添加一个调试图形对象到场景
   * @param scene Three.js场景
   * @param object 调试图形对象
   */
  public static addDebugObject(scene: THREE.Scene, object: THREE.Object3D): void {
    if (!Debug.enabled) return;
    
    scene.add(object);
    Debug.debugObjects.push(object);
    
    // 设置可见性
    object.visible = Debug.helpersVisible;
  }
  
  /**
   * 移除所有调试图形对象
   * @param scene Three.js场景
   */
  public static removeAllDebugObjects(scene: THREE.Scene): void {
    for (const obj of Debug.debugObjects) {
      scene.remove(obj);
    }
    Debug.debugObjects = [];
  }
  
  /**
   * 设置调试辅助器的可见性
   * @param visible 是否可见
   */
  public static setHelpersVisible(visible: boolean): void {
    Debug.helpersVisible = visible;
    
    for (const obj of Debug.debugObjects) {
      obj.visible = visible;
    }
  }
  
  /**
   * 创建一个坐标轴辅助器
   * @param size 坐标轴大小
   * @returns 坐标轴辅助器
   */
  public static createAxesHelper(size: number = 5): THREE.AxesHelper {
    return new THREE.AxesHelper(size);
  }
  
  /**
   * 创建一个网格辅助器
   * @param size 网格大小
   * @param divisions 网格分割数
   * @returns 网格辅助器
   */
  public static createGridHelper(size: number = 10, divisions: number = 10): THREE.GridHelper {
    return new THREE.GridHelper(size, divisions);
  }
  
  /**
   * 打印对象的详细信息
   * @param object 要打印的对象
   * @param label 对象标签
   */
  public static inspect(object: any, label: string = 'Object'): void {
    if (!Debug.enabled) return;
    
    const json = JSON.stringify(object, null, 2);
    console.group(`[检查] ${label}`);
    console.log(json);
    console.groupEnd();
  }
  
  /**
   * 性能监控：开始测量某个操作的性能
   * @param name 操作名称
   */
  public static perf(name: string): void {
    if (!Debug.enabled) return;
    
    Debug.perfMarks[name] = performance.now();
  }
  
  /**
   * 性能监控：结束测量某个操作的性能
   * @param name 操作名称
   * @returns 操作耗时（毫秒）
   */
  public static perfEnd(name: string): number {
    if (!Debug.enabled) return 0;
    
    if (!Debug.perfMarks[name]) {
      Debug.warn(`未找到性能测量起点: ${name}`);
      return 0;
    }
    
    const startTime = Debug.perfMarks[name];
    const endTime = performance.now();
    const duration = endTime - startTime;
    
    // 存储测量结果
    if (!Debug.perfMeasures[name]) {
      Debug.perfMeasures[name] = [];
    }
    
    Debug.perfMeasures[name].push(duration);
    
    // 保持测量历史不超过100个
    if (Debug.perfMeasures[name].length > 100) {
      Debug.perfMeasures[name].shift();
    }
    
    return duration;
  }
  
  /**
   * 获取性能测量的平均值
   * @param name 操作名称
   * @returns 平均耗时（毫秒）
   */
  public static perfAverage(name: string): number {
    if (!Debug.enabled || !Debug.perfMeasures[name] || Debug.perfMeasures[name].length === 0) {
      return 0;
    }
    
    const sum = Debug.perfMeasures[name].reduce((a, b) => a + b, 0);
    return sum / Debug.perfMeasures[name].length;
  }
  
  /**
   * 清除所有性能测量数据
   */
  public static clearPerfMeasures(): void {
    Debug.perfMarks = {};
    Debug.perfMeasures = {};
  }
} 